﻿using System.Collections.Generic;
using System.Linq;
using Godot.core.os;
using Godot.core.io;

namespace Godot.core.bind
{
    using real_t = System.Double;
    using SysMethodInfo = System.Reflection.MethodInfo;

    public partial class File : Reference
    {
        public enum ModeFlags
        {
            READ = 1,
            WRITE = 2,
            READ_WRITE = 3,
        };

        public Error open(string p_path, int p_mode_flags) 
        {
	        close();
	        Error err = Error.FAILED;
	        __f = FileAccess.open(p_path,(FileAccess.ModeFlags) p_mode_flags, ref err);
	        if (__f != null)
		        __f.set_endian_swap(__eswap);
	        return err;
        }

        public void close()
        {
            __f.close();
            __f = null;
        }

        public bool is_open() 
        {
	        return __f != null;
        }

        public void seek(uint p_position)
        {
	        if(__f == null) return;
            __f.seek(p_position);
        }
        
        public void seek_end(uint p_position)
        {
	        if(__f == null) return;
	        __f.seek_end(p_position);
        }

        public uint get_pos()
        {
	        if(__f == null) return 0;
	        return __f.get_pos();
        }

        public uint get_len()
        {
	        if(__f == null) return 0;
	        return __f.get_len();
        }

        public bool eof_reached() 
        {
	        if(null == __f) return false;
	        return __f.eof_reached();
        }

        public byte get_8()
        {
	        if(null == __f) return 0;
	        return __f.get_8();
        }

        public ushort get_16()
        {
	        if(null == __f) return 0;
	        return __f.get_16();
        }

        public uint get_32()
        {
	        if(null == __f) return 0;
	        return __f.get_32();
        }

        public ulong get_64()
        {
	        if(null == __f) return 0;
	        return __f.get_64();
        }

        public float get_float()
        {
	        if(null == __f) return 0.0f;
	        return __f.get_float();
        }

        public double get_double()
        {
	        if(null == __f) return 0;
	        return __f.get_double();
        }

        public real_t get_real()
        {
	        if(null == __f) return 0;
	        return __f.get_real();
        }

        public List<byte> get_buffer(int p_length)
        {
            List<byte> data = new List<byte>();
	        if(null == __f) return data;

	        if(p_length <= 0) return data;
	        
	        int len = __f.get_buffer(ref data, p_length);
	        
            if( len < 0 ) return  new List<byte>();

            if (len < p_length)
                data.AddRange(Enumerable.Repeat<byte>(0, p_length - len));

	        return data;
        }

        public string get_as_text()
        {
	        string text = "";
	        string l = get_line();
	        while(!eof_reached()) 
            {
		        text += l + "\n";
		        l = get_line();
	        }
	        return text;
        }

        public string get_line()
        {
	        if(null == __f) return "";
	        return __f.get_line();
        }

        public List<string> get_csv_line()
        {
	        if(null == __f) return new List<string>();
	        return __f.get_csv_line();
        }

        public bool get_endian_swap()
        {
            return __eswap;
        }

        public void set_endian_swap(bool p_swap)
        {
	        __eswap = p_swap;
	        if (__f != null)
		        __f.set_endian_swap(p_swap);
        }

        public Error get_error()
        {
	        if (null == __f)
		        return Error.ERR_UNCONFIGURED;
	        return __f.get_error();
        }

        public Variant get_var()
        {
	        if(null == __f)  return new Variant();
	        
            uint len = get_32();
            uint r_len = 0;
	        
            List<byte> buff = get_buffer((int)len);
	        
            if(buff.Count != len) return new Variant();

            byte[] arrbuff = buff.ToArray();

            Variant v = new Variant();
            unsafe
            {
                fixed (byte* tmp = &arrbuff[0])
                {
                    Error err = Marshalls.decode_variant(ref v, tmp, len, ref r_len);
                    if(err != Error.OK) return new Variant();
                }                  
            }

	        return v;
        }
        
        public void store_8(byte p_dest)
        {
	        if(null == __f) return;
	        __f.store_8(p_dest);
        }

        public void store_16(ushort p_dest)
        {
	        if(null == __f) return;
	        __f.store_16(p_dest);
        }

        public void store_32(uint p_dest)
        {
	        if(null == __f) return;
	        __f.store_32(p_dest);
        }

        public void store_64(ulong p_dest)
        {
	        if(null == __f) return;
	        __f.store_64(p_dest);
        }

        public void store_float(float p_dest)
        {
	        if(null == __f) return;
	        __f.store_float(p_dest);
        }

        public void store_double(double p_dest)
        {
	        if(null == __f) return;
	        __f.store_double(p_dest);
        }

        public void store_real(real_t p_real)
        {
	        if(null == __f) return;
	        __f.store_real(p_real);
        }

        public void store_string(string p_string)
        {
	        if(null == __f) return;
	        __f.store_string(p_string);
        }

        public void store_line(string p_line) 
        {
	        store_string(p_line);
	        store_8((byte)'\n');
        }

        public void store_buffer(List<byte> p_buffer)
        {
	        if(null == __f) return;
	        __f.store_buffer(p_buffer.ToArray());
        }

        public void store_var(Variant p_var) 
        {
            throw new System.NotImplementedException();
        }

        public void store_pascal_string(string p_string) 
        {
	        if(null == __f) return;
	        __f.store_pascal_string(p_string);
        }

        public string get_pascal_string() 
        {
	        throw new System.NotImplementedException();
        }

        public bool file_exists(string p_name)
        {
	        return FileAccess.exists(p_name);
        }

        protected static new void _bind_methods()
        {
            ObjectTypeDB.bind_method(_MD("open","path","flags"), typeof(File).GetMethod("open", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("close"),typeof(File).GetMethod("close", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("is_open"),typeof(File).GetMethod("is_open", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("seek","pos"),typeof(File).GetMethod("seek", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("seek_end","pos"),typeof(File).GetMethod("seek_end", Misc.BindingFlags_Greedy), new Variant(0));
	        ObjectTypeDB.bind_method(_MD("get_pos"),typeof(File).GetMethod("get_pos", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("get_len"),typeof(File).GetMethod("get_len", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("eof_reached"),typeof(File).GetMethod("eof_reached", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("get_8"),typeof(File).GetMethod("get_8", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("get_16"),typeof(File).GetMethod("get_16", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("get_32"),typeof(File).GetMethod("get_32", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("get_64"),typeof(File).GetMethod("get_64", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("get_float"),typeof(File).GetMethod("get_float", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("get_double"),typeof(File).GetMethod("get_double", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("get_real"),typeof(File).GetMethod("get_real", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("get_buffer","len"),typeof(File).GetMethod("get_buffer", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("get_line"),typeof(File).GetMethod("get_line", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("get_as_text"),typeof(File).GetMethod("get_as_text", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("get_endian_swap"),typeof(File).GetMethod("get_endian_swap", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("set_endian_swap","enable"),typeof(File).GetMethod("set_endian_swap", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("get_error:Error"),typeof(File).GetMethod("get_error", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("get_var"),typeof(File).GetMethod("get_var", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("get_csv_line"),typeof(File).GetMethod("get_csv_line", Misc.BindingFlags_Greedy));

	        ObjectTypeDB.bind_method(_MD("store_8","value"),typeof(File).GetMethod("store_8", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("store_16","value"),typeof(File).GetMethod("store_16", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("store_32","value"),typeof(File).GetMethod("store_32", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("store_64","value"),typeof(File).GetMethod("store_64", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("store_float","value"),typeof(File).GetMethod("store_float", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("store_double","value"),typeof(File).GetMethod("store_double", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("store_real","value"),typeof(File).GetMethod("store_real", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("store_buffer","buffer"),typeof(File).GetMethod("store_buffer", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("store_line","line"),typeof(File).GetMethod("store_line", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("store_string","string"),typeof(File).GetMethod("store_string", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("store_var","value"),typeof(File).GetMethod("store_var", Misc.BindingFlags_Greedy));

	        ObjectTypeDB.bind_method(_MD("store_pascal_string","string"),typeof(File).GetMethod("store_pascal_string", Misc.BindingFlags_Greedy));
	        ObjectTypeDB.bind_method(_MD("get_pascal_string"),typeof(File).GetMethod("get_pascal_string", Misc.BindingFlags_Greedy));

	        ObjectTypeDB.bind_method(_MD("file_exists","path"),typeof(File).GetMethod("file_exists", Misc.BindingFlags_Greedy));

            ObjectTypeDB.bind_integer_constant(get_type_static(), "READ", (int)ModeFlags.READ);
            ObjectTypeDB.bind_integer_constant(get_type_static(), "WRITE", (int)ModeFlags.WRITE);
            ObjectTypeDB.bind_integer_constant(get_type_static(), "READ_WRITE", (int)ModeFlags.READ_WRITE);
        }

        private FileAccess __f;
        private bool __eswap;
    }

    public partial class File
    {
        #region OBJ_TYPE(File, Reference)
        public override string get_type() { return "File"; }

        public override string get_save_type() { return get_type(); } //type stored when saving

        public override StringName get_type_name() { return (StringName)get_type_static(); }

        public override bool is_type(string p_type) { return (p_type == "File") ? true : base.is_type(p_type); }

        protected override void _initialize_typev() { initialize_type(); }

        protected override bool _setv(StringName p_name, Variant p_property)
        {
            if (base._setv(p_name, p_property))
                return true;

            if (typeof(File).GetMethod("_set", Misc.BindingFlags_Greedy) != typeof(Reference).GetMethod("_set", Misc.BindingFlags_Greedy))
            {
                return _set(p_name, p_property);
            }

            return false;
        }

        protected override bool _getv(StringName p_name, ref Variant r_property)
        {
            if (typeof(File).GetMethod("_get", Misc.BindingFlags_Greedy) != typeof(Reference).GetMethod("_get", Misc.BindingFlags_Greedy))
            {
                if (_get(p_name, ref r_property))
                    return true;
            }

            return base._getv(p_name, ref r_property);
        }

        protected override void _get_property_listv(ref List<PropertyInfo> p_list, bool p_reversed)
        {
            if (!p_reversed)
            {
                base._get_property_listv(ref p_list, p_reversed);
            }

            p_list.Add(new PropertyInfo(Variant.Type.NIL, get_type_static(), PropertyHint.PROPERTY_HINT_NONE, "", (int)PropertyUsageFlags.PROPERTY_USAGE_CATEGORY));

            if (!_is_gpl_reversed())
                ObjectTypeDB.get_property_list("File", ref p_list, true);

            if (typeof(File).GetMethod("_get_property_list", Misc.BindingFlags_Greedy) != typeof(Reference).GetMethod("_get_property_list", Misc.BindingFlags_Greedy))
            {
                _get_property_list(ref p_list);
            }

            if (_is_gpl_reversed())
                ObjectTypeDB.get_property_list("File", ref p_list, true);

            if (p_reversed)
            {
                base._get_property_listv(ref p_list, p_reversed);
            }
        }

        protected override void _notificationv(int p_notification, bool p_reversed)
        {
            if (!p_reversed)
                base._notificationv(p_notification, p_reversed);

            if (typeof(File).GetMethod("_notification", Misc.BindingFlags_Greedy) != typeof(Reference).GetMethod("_notification", Misc.BindingFlags_Greedy))
            {
                _notification(p_notification);
            }

            if (p_reversed)
                base._notificationv(p_notification, p_reversed);
        }

        public static new string get_type_static() { return "File"; }

        public static new string get_parent_type_static() { return Reference.get_type_static(); }

        public static new void get_inheritance_list_static(ref List<string> r_inheritance_list)
        {
            Reference.get_inheritance_list_static(ref r_inheritance_list);
            r_inheritance_list.Add(get_type_static());
        }

        public static new string get_category_static()
        {
            string category = Reference.get_category_static();
            if (typeof(File).GetMethod("_get_category", Misc.BindingFlags_Greedy) != typeof(Reference).GetMethod("_get_category", Misc.BindingFlags_Greedy))
            {
                if (category != "")
                    category += "/";
                category += _get_category();
            }
            return category;
        }

        public static new string inherits_static() { return "Reference"; }

        public static new void get_valid_parents_static(ref List<string> p_parents)
        {
            if (typeof(File).GetMethod("_get_valid_parents_static", Misc.BindingFlags_Greedy) != typeof(Reference).GetMethod("_get_valid_parents_static", Misc.BindingFlags_Greedy))
            {
                File._get_valid_parents_static(ref p_parents);
            }

            Reference.get_valid_parents_static(ref p_parents);
        }

        public static new void initialize_type()
        {
            if (__initialized)
                return;
            Reference.initialize_type();
            ObjectTypeDB._add_type<File>();

            SysMethodInfo cls_bind = typeof(File).GetMethod("_bind_methods", Misc.BindingFlags_Greedy);
            SysMethodInfo par_bind = typeof(Reference).GetMethod("_bind_methods", Misc.BindingFlags_Greedy);
            if (cls_bind != par_bind)
                cls_bind.Invoke(null, null);
            __initialized = true;
        }

        private static bool __initialized = false;
        #endregion
    }
}
